package dev.ece.suit.loader;

import java.util.Observable;
import java.util.Observer;

import com.bulletphysics.collision.dispatch.CollisionObject;
import com.bulletphysics.collision.dispatch.PairCachingGhostObject;
import com.bulletphysics.collision.shapes.CollisionShape;
import com.bulletphysics.collision.shapes.ConvexShape;

import dev.ece.core.cells.model.action.ActionManager;
import dev.ece.core.cells.model.action.ActionManager.ActionListener;
import dev.ece.core.sprite.Sprite;
import dev.ece.util.jbullet.EceJbulletWorld;
import dev.ece.util.lib.Vector3f;
import dev.ece.util.lib.Vector4f;
import dev.ece.util.obj.ObjectCount;
import dev.ece.util.tools.Camera;
import dev.ece.util.tools.transformations.JbulletTransformation;

public class Sprite3D<T extends Number & Comparable<T>> extends Sprite<Frame<T>, AbstractModel3D<T>> {
	
	private Frame<T> frame;
	
	private ObjectCount<? extends AbstractModel3D<T>> modelCount;
	
	private JbulletTransformation jbulletTransformation = new JbulletTransformation();
	
	private float mass;
	
	public Sprite3D(AbstractModel3D<T> model) {
		this(createSimpleObjectCount(model));
	}
	
	public Sprite3D(ObjectCount<? extends AbstractModel3D<T>> modelCount) {
		super(modelCount.getObject());
		this.modelCount = modelCount;
		this.frame = modelCount.getObject().createFrameInstance();
		this.setValue(this.frame);
		this.transformations.add(jbulletTransformation);
		this.setAlpha(this.getModel().isAlpha());
	}

	protected static <T extends Number & Comparable<T>> ObjectCount<AbstractModel3D<T>> createSimpleObjectCount(AbstractModel3D<T> model) {
		ObjectCount<AbstractModel3D<T>> result = new ObjectCount<AbstractModel3D<T>>("", model);
		result.addCount();
		result.addObserver(new Observer() {

			@Override
			@SuppressWarnings("unchecked")
			public void update(Observable o, Object arg) {
				ObjectCount<AbstractModel3D<T>> count = (ObjectCount<AbstractModel3D<T>>) o;
				if(count.needDestroy()) {
					count.getObject().destroy();
				}
			}
			
		});
		return result;
	}
	
	public CollisionObject addToEceJbulletWorld(EceJbulletWorld world) {
		return addToEceJbulletWorld(world, null);
	}
	
	public CollisionObject addToEceJbulletWorld(EceJbulletWorld world, Vector3f position) {
		return addToEceJbulletWorld(world, position, null, null);
	}
	
	public CollisionObject addToEceJbulletWorld(EceJbulletWorld world, Vector3f position, Vector4f rotation, Vector3f scale) {
		removeFromEceJbulletWorld(world);
		CollisionShape shape = this.getModel().createCollisionShape();
		if(scale != null) {
			shape.setLocalScaling(scale.toVector3f());
			jbulletTransformation.setScale(scale);
		}
		jbulletTransformation.setCollisionObject(
				world.addRigidBody(shape, mass, null, 
						position != null ? position.toVector3f() : null, rotation != null ? rotation.toQuat4f() : null));
		jbulletTransformation.getCollisionObject().setUserPointer(this);
		return jbulletTransformation.getCollisionObject();
	}
	
	public void removeFromEceJbulletWorld(EceJbulletWorld world) {
		if(jbulletTransformation.getCollisionObject() != null) {
			world.removeCollisionObject(jbulletTransformation.getCollisionObject());
			jbulletTransformation.removeCollisionObject();
		}
	}
	
	public PairCachingGhostObject addGhostObjectToEceJbulletWorld(EceJbulletWorld world) {
		return addGhostObjectToEceJbulletWorld(world, null);
	}
	
	public PairCachingGhostObject addGhostObjectToEceJbulletWorld(EceJbulletWorld world, Vector3f position) {
		return addGhostObjectToEceJbulletWorld(world, position, null, null);
	}
	
	public PairCachingGhostObject addGhostObjectToEceJbulletWorld(EceJbulletWorld world, Vector3f position, Vector4f rotation, Vector3f scale) {
		removeFromEceJbulletWorld(world);
		ConvexShape shape = (ConvexShape) this.getModel().createCollisionShape();
		if(scale != null) {
			shape.setLocalScaling(scale.toVector3f());
			jbulletTransformation.setScale(scale);
		}
		jbulletTransformation.setCollisionObject(world.addGhostObject(shape, mass, null, 
				position != null ? position.toVector3f() : null, rotation != null ? rotation.toQuat4f() : null));
		jbulletTransformation.getCollisionObject().setUserPointer(this);
		return (PairCachingGhostObject) jbulletTransformation.getCollisionObject();
	}
	
	@Override
	public void onLogic(Camera camera) {
		frame.logic();
	}
	
	public boolean hasAction(String name) {
		return this.getModel().getActions().containsKey(name);
	}
	
	public Joint<T>[] getJoints() {
		return this.getModel().getJoints();
	}
	
	public int indexOfJoint(String name) {
		return this.getModel().indexOfJoint(name);
	}
	
	public void action(String name) {
		getActionManager().action(name, this.getModel().getActions());
	}
	
	public void stop() {
		getActionManager().stop();
	}
	
	public ActionListener<T> getActionListener() {
		return getActionManager().getActionListener();
	}
	
	public void setActionListener(ActionListener<T> actionListener) {
		getActionManager().setActionListener(actionListener);
	}
	
	public String getActionName() {
		return getActionManager().getAction() != null ? getActionManager().getAction().getName() : null;
	}

	public int[] getMeshIndice() {
		return frame.getIndice();
	}

	public void setMeshIndice(int[] indice) {
		frame.setIndice(indice);
	}

	public ActionManager<T> getActionManager() {
		return frame.getActionManager();
	}

	public ObjectCount<? extends AbstractModel3D<T>> getModelCount() {
		return modelCount;
	}

	public float getMass() {
		return mass;
	}

	public void setMass(float mass) {
		this.mass = mass;
	}

	public JbulletTransformation getJbulletTransformation() {
		return jbulletTransformation;
	}
	
	public void destroy() {
		this.setModel(null);
		this.modelCount.deleteCount();
		super.destroy();
	}
	
	@Override
	public synchronized void setModel(AbstractModel3D<T> model) {
		this.setInitialized(false);
		super.setModel(model);
		if(model != null) {
			this.frame = model.createFrameInstance();
			this.setValue(frame);
		}
	}
	
}
